package com.hunnyovo.springframework.beans.factory.support;

import cn.hutool.core.bean.BeanUtil;
import com.hunnyovo.springframework.beans.BeansException;
import com.hunnyovo.springframework.beans.PropertyValue;
import com.hunnyovo.springframework.beans.PropertyValues;
import com.hunnyovo.springframework.beans.factory.config.BeanDefinition;
import com.hunnyovo.springframework.beans.factory.config.BeanReference;

import java.lang.reflect.Constructor;

/**
 * 抽象自动装配Bean工厂
 *
 * @author Create By Cb
 * @date 2022/10/26 18:36
 **/
abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory {

    private final InstantiationStrategy instantiationStrategy = new SimpleInstantiationStrategy();

    @Override
    protected Object createBean(String beanName, BeanDefinition beanDefinition, Object... args) throws BeansException {
        Object bean = this.createBeanInstance(beanDefinition, beanName, args);
        //  注入属性
        this.applyPropertyValues(beanName, bean, beanDefinition);
        this.addSingletonBean(beanName, bean);
        return bean;
    }

    /**
     * 创建bean实例注入属性
     *
     * @param beanDefinition:
     * @param beanName:
     * @param args:
     * @return {@link Object}
     * @author Create By Cb
     * @date 2022/11/18 19:30
     */
    protected Object createBeanInstance(BeanDefinition beanDefinition, String beanName, Object... args) throws BeansException {
        Constructor<?> constructor = null;
        Class<?> clazz = beanDefinition.getBeanClass();
        //  取beanDefinition中类下的所有构造函数
        Constructor<?>[] declarations = clazz.getDeclaredConstructors();
        for (Constructor<?> ctor : declarations) {
            //  找到所有构造函数中 入参数量和args长度一样的构造方法 后续进行属性注入
            if (ctor != null && ctor.getParameterCount() == args.length) {
                constructor = ctor;
                break;
            }
        }
        return this.instantiationStrategy.instantiation(beanDefinition, beanName, constructor, args);
    }

    /**
     * 向bean注入属性
     *
     * @param beanName:
     * @param bean:
     * @param beanDefinition:
     * @author Create By Cb
     * @date 2022/11/23 16:33
     */
    protected void applyPropertyValues(String beanName, Object bean, BeanDefinition beanDefinition) throws BeansException {
        try {
            PropertyValues propertyValues = beanDefinition.getPropertyValues();
            for (PropertyValue propertyValue : propertyValues.getPropertyValues()) {
                String name = propertyValue.getName();
                Object value = propertyValue.getValue();
                //  判断是不是一个bean
                if (value instanceof BeanReference) {
                    BeanReference beanReference = (BeanReference) value;
                    value = this.getBean(beanReference.getBeanName());
                }
                //  填充属性
                BeanUtil.setFieldValue(bean, name, value);
            }
        } catch (Exception e) {
            throw new BeansException("ApplyPropertyValues Fail Bean:[]" + beanName);
        }
    }

}
